#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdalign.h>
#include <unistd.h>

const std::string gpath = "/home/shenyu";
int gprojId = 0x6666;
int gshmsize = 4096;
// 共享内存也有权限
mode_t gmode = 0600;

std::string ToHex(key_t k)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%x", k);
    return buffer;
}

// 共享内存封装
class ShareMemory
{
private:
    // 服务端和客户端都要获取shm只是两者传参不同
    // 封装起来代码复用
    void CreatShmHelper(int flag)
    {
        _key = ::ftok(gpath.c_str(), gprojId);
        if (_key < 0)
        {
            std::cerr << "ftok error" << std::endl;
            return;
        }
        _shmid = ::shmget(_key, gshmsize, flag);
        if (_shmid < 0)
        {
            std::cerr << "shmget error" << std::endl;
            return;
        }
        std::cout << "shmid:" << _shmid << std::endl;
    }

public:
    ShareMemory()
        : _shmid(-1), _key(0), _addr(nullptr)
    {
    }
    ~ShareMemory()
    {
    }
    // 服务端用来创建共享内存的
    void CreatShm()
    {
        // 避免重复创建
        if (_shmid == -1)
        {
            // 没有就创建,若已经存在则失败返回
            // 成功必定是一个新的共享内存
            CreatShmHelper(IPC_CREAT | IPC_EXCL | gmode);
        }
        std::cout << "key: " << ToHex(_key) << std::endl;
    }
    // 客户端用来获取服务端创建的共享内存的
    void GetShm()
    {
        // 没有就创建,有就拿来用
        CreatShmHelper(IPC_CREAT);
        std::cout << "key: " << ToHex(_key) << std::endl;
    }
    // 挂接共享内存
    void AttachShm()
    {
        // 参数nullptr由系统自动选择可用地址
        // 0以读写的方式附加
        // 返回共享内存附加在进程中的起始地址
        _addr = shmat(_shmid, nullptr, 0);
        // long long防溢出
        if ((long long)_addr == -1)
        {
            std::cerr << "shmat error" << std::endl;
            return;
        }
    }
    //解除挂接
    void DetachShm()
    {
        if (_addr != nullptr)
        {
            ::shmdt(_addr);
        }
        std::cout<<"Detach Done"<<std::endl;
    }
    //删除共享内存
    void DeleteShm()
    {
        shmctl(_shmid,IPC_RMID,nullptr);
    }
    void* GetAddr()
    {
        return _addr;
    }
    //打印shm状态
    void ShmMeta()
    {
        struct shmid_ds buffer; //系统提供的数据类型
        //获取shm状态保存在buffer中
        int n=shmctl(_shmid,IPC_STAT,&buffer);
        if(n<0)
        {
            return ;
        }
        std::cout<<"#####################"<<std::endl;
        //最后一次访问时间(不准确)
        std::cout << buffer.shm_atime << std::endl;
        //创建者进程id
        std::cout << buffer.shm_cpid << std::endl;
        //最后修改时间
        std::cout << buffer.shm_ctime << std::endl;
        //当前附加数
        std::cout << buffer.shm_nattch << std::endl;
        //共享内存权限键,通常与 shmget 调用时指定的键（key）相关
        std::cout << ToHex(buffer.shm_perm.__key) << std::endl;
        std::cout << "################" << std::endl;
    }
private:
    int _shmid;
    key_t _key;
    // 这里是void*,在使用时要强转
    void *_addr;
};

//临时
ShareMemory shm;

struct data
{
    char status[32];
    char lasttime[48];
    char image[4000];
};